package com.atguigu.gmall.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.IpUtil;
import org.apache.http.HttpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.List;

@Component
public class AuthGlobalFilter implements GlobalFilter {

    @Value("${authUrls.url}")
    private String authUrls;
    @Autowired
    private RedisTemplate redisTemplate;
    // 匹配路径的工具类
    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        System.out.println("request.getURI(): " + request.getURI());
        System.out.println("request.getURI().getPath(): " + request.getURI().getPath());
        String path = request.getURI().getPath();
        //如果是内部接口, 则拦截不允许外部访问
        if (antPathMatcher.match("/**/inner/**", path)) {
            ServerHttpResponse response = exchange.getResponse();
            return out(response, ResultCodeEnum.PERMISSION);
        }
        // 获取用户Id
        String userId = getUserId(request);
        String userTempId = getUserTempId(request);
        //token被盗用
        if ("-1".equals(userId)) {
            ServerHttpResponse response = exchange.getResponse();
            return out(response, ResultCodeEnum.PERMISSION);
        }

        //api接口，异步请求，校验用户必须登录
        if (antPathMatcher.match("/api/**/auth/**", path)) {
            if (StringUtils.isEmpty(userId)) {
                ServerHttpResponse response = exchange.getResponse();
                return out(response, ResultCodeEnum.LOGIN_AUTH);
            }
        }

        //  判断用户是否访问了 trade.html,myOrder.html 这样的控制器时，必须要登录！
        //  authUrlsUrl= trade.html,myOrder.html,list.html
        //  校验的规则：http://www.gmall.com/index.html 不需要  http://list.gmall.com/list.html?category3Id=61 需要！

        String[] split = authUrls.split(",");
        for (String url : split) {
            //  用户未登录！并且访问的控制器是需要登录的！
            if (path.indexOf(url) != -1 && StringUtils.isEmpty(userId)) {
                ServerHttpResponse response = exchange.getResponse();
                // 303状态码表示由于请求对应的资源存在着另一个URI，应使用重定向获取请求的资源
                response.setStatusCode(HttpStatus.SEE_OTHER);
                response.getHeaders().set(HttpHeaders.LOCATION, "http://www.gmall.com/login.html?originUrl="+request.getURI());
                return response.setComplete();
            }
        }

        //  验证通过之后：将用户的Id 传递给后台微服务！
        if (!StringUtils.isEmpty(userId) ||!StringUtils.isEmpty(userTempId)) {
            if (!StringUtils.isEmpty(userId) ){
                //  需要将用户Id 放入请求头中!
                request.mutate().header("userId", userId).build();
            }
            if(!StringUtils.isEmpty(userTempId)){
                //  需要将用户Id 放入请求头中!
                request.mutate().header("userTempId", userTempId).build();
            }
            return chain.filter(exchange.mutate().request(request).build());
        }
        //  默认返回
        return chain.filter(exchange);
    }
    //  获取临时用户Id
    private String getUserTempId(ServerHttpRequest request) {
        String userTempId = "";
        HttpCookie httpCookie = request.getCookies().getFirst("userTempId");
        if (httpCookie != null) {
            userTempId = httpCookie.getValue();
        } else {
            List<String> stringList = request.getHeaders().get("userTempId");
            if (!CollectionUtils.isEmpty(stringList)){
                userTempId = stringList.get(0);
            }
        }

        return userTempId;
    }

    /**
     * 获取当前登录用户id
     *
     * @param request
     * @return
     */
    private String getUserId(ServerHttpRequest request) {
        String token = "";
        List<String> tokenList = request.getHeaders().get("token");
        if (tokenList != null) {
            token = tokenList.get(0);
        } else {
            MultiValueMap<String, HttpCookie> cookiesMultiValueMap = request.getCookies();
            HttpCookie httpCookie = cookiesMultiValueMap.getFirst("token");
            if (httpCookie != null) {
                System.out.println("httpCookie.getValue():"+httpCookie.getValue());
                token = URLDecoder.decode(httpCookie.getValue());
                System.out.println("URLDecoder.decode():"+token);
            }
        }

        if (!StringUtils.isEmpty(token)) {
            String userInfoStr = (String) redisTemplate.opsForValue().get("user:login:" + token);
            JSONObject jsonObject = JSONObject.parseObject(userInfoStr);
            String ip = jsonObject.getString("ip");
            String curIp = IpUtil.getGatwayIpAddress(request);
            //校验token是否被盗用
            if (ip.equals(curIp)) {
                return jsonObject.getString("userId");
            } else {
                return "-1";
            }
        }
        return "";
    }

    // 接口鉴权失败返回数据
    private Mono<Void> out(ServerHttpResponse response, ResultCodeEnum resultCodeEnum) {
        // 返回用户没有权限登录
        Result<Object> result = Result.build(null, resultCodeEnum);
        //  将result 变成json 字符串！ 如果出现字符集的问题！
        String jsonString = JSONObject.toJSONString(result);
        DataBuffer wrap = response.bufferFactory().wrap(jsonString.getBytes());

        //  使用响应对象设置响应头
        response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
        return response.writeWith(Mono.just(wrap));
    }
}
